<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <!-- <meta http-equiv="X-UA-Compatible" content="IE=edge"> -->
  <meta http-equiv="Access-Control-Allow-Origin" content="*" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>websocket用户界面</title>
  <link rel="icon" href="./favicon.ico">
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    body,
    html {
      width: 100%;
      height: 100%;
      position: relative;
    }

    #content {
      width: 100%;
      position: relative;
      height: 100%;
      overflow: hidden;
    }

    #views {
      position: absolute;
      top: 0;
      left: 0;
      transition: all .5s ease;
      width: 200%;
      height: 100%;
    }

    #leftbox {
      box-sizing: border-box;
      position: relative;
      float: left;
      width: 50%;
      height: 100%;
      background-color: white;
    }

    .headClass {
      display: flex;
      justify-content: center;
      flex-direction: column;
      align-items: center;
      height: 80px;
      background-color: skyblue;
    }

    .headuser {
      color: white;
      margin-bottom: 5px;
    }

    #rightbox {
      box-sizing: border-box;
      float: left;
      position: relative;
      width: 50%;
      height: 100%;
      background-color: whitesmoke;
      display: flex;
      flex-direction: column;
      align-items: center;
    }

    .titleClass {
      text-align: center;
      height: 45px;
      width: 100%;
      line-height: 45px;
      background-color: skyblue;
      color: white;
    }

    .titleClass::before {
      content: '';
      width: 10px;
      height: 10px;
      position: absolute;
      left: 11px;
      top: 15px;
      border-width: 1px;
      border-style: solid;
      border-color: black transparent transparent black;
      transform: rotate(-45deg);
    }


    #chatcontent {
      width: 100%;
      height: auto;
      flex: 1;
      overflow-y: auto;
    }

    #chatcontent::-webkit-scrollbar {
      width: 5px;
    }

    #chatcontent::-webkit-scrollbar-track {
      -webkit-webkit: box shadow inset 6pxrgba;
      -moz-webkit: box shadow inset 6pxrgba;
      -ms-webkit: box shadow inset 6pxrgba;
      -o-webkit: box shadow inset 6pxrgba;
      webkit: box shadow inset 6pxrgba;
      border-radius: 10px;
    }

    #chatcontent::-webkit-scrollbar-thumb {
      border-radius: 10px;
      background: rgba(0, 0, 0, 0.1);
      -webkit-webkit: box shadow inset 6;
      -moz-webkit: box shadow inset 6;
      -ms-webkit: box shadow inset 6;
      -o-webkit: box shadow inset 6;
      webkit: box shadow inset 6;
    }

    .inputClass {
      width: 100%;
      padding: 8px;
      font-size: 12px;
      color: black;
      display: flex;
      border-top: 1px solid gray;
    }

    .inputClass #inputchat {
      flex: 1;
      padding-left: 10px;
      height: 30px;
      margin: 0 10px;
      border-radius: 20px;
      border: none;
    }

    .inputClass #inputchat:focus {
      outline: none;
    }

    .inputClass #tosend {
      height: 30px;
      padding: 0 10px;
      border-radius: 10px;
      border: 1px solid gray;
    }

    .headimg {
      border-radius: 50%;
      width: 30px;
      height: 30px;
      border: 1px solid white;
    }

    .userClass {
      padding: 5px;
      margin: 5px;
      background: whitesmoke;
      list-style: none;
      display: flex;
      justify-content: space-between;
      align-items: center;
      height: 50px;
      border-radius: 3px;
    }

    .userClass>p {
      font-size: 20px;
      color: gray;
    }

    .userClass>div {
      display: flex;
      justify-content: space-around;
      align-items: center;
      flex-direction: column;
      color: gray;
      height: 100%;
    }

    .redTip {
      display: inline-block;
      background-color: red;
      border-radius: 50%;
      padding: 2px;
      width: 15px;
      height: 15px;
      font-size: 10px;
      text-align: center;
      vertical-align: middle;
      line-height: 15px;
      visibility: hidden;
      color: white;
    }


    /* 新的 */

    .chatClass {
      display: flex;
      padding: 5px;
    }

    .me {
      margin: 5px 5px 5px 45px;
      justify-content: flex-end;
    }

    .meuserimg {
      width: 30px;
      height: 30px;
      margin-left: 10px;
    }

    .mediv {
      text-align: right;
    }

    span.me_icon::before {
      content: '';
      border-width: 5px;
      border-style: solid;
      border-color: transparent transparent transparent white;
      width: 0;
      height: 0;
      position: absolute;
      right: -9px;
      top: 12px;
    }

    .other {
      margin: 5px 45px 5px 5px;
      justify-content: flex-start;
    }

    .otheruserimg {
      width: 30px;
      height: 30px;
      margin-right: 10px;
    }

    .otherdiv {
      text-align: left;
    }

    span.other_icon::before {
      content: '';
      border-width: 5px;
      border-style: solid;
      border-color: transparent white transparent transparent;
      width: 0;
      height: 0;
      position: absolute;
      left: -9px;
      top: 12px;
    }

    .message_content {
      font-size: 14px;
      display: inline-block;
      padding: 6px;
      background-color: white;
      color: black;
      border-radius: 5px;
      text-align: left;
      line-height: 18px;
      position: relative;
    }

    .username {
      font-size: 12px;
    }
  </style>
</head>

<body>
  <script src='./js/base.js'></script>
  <div id='content'>
    <div id='views'>
      <div id="leftbox">
        <div class='headClass'>
          <p class='headuser' id='headuser'></p>
          <img class='headimg' src="favicon.ico" alt="">
        </div>
        <ul id="userList">
        </ul>
      </div>

      <div id="rightbox">
        <div id="title" class='titleClass' onclick='toback()'></div>
        <div id="chatcontent">
        </div>
        <div class='inputClass'>
          <input type="text" name="" id="inputchat" placeholder="说点什么。。。">
          <button id='tosend' onclick='tosend()'>发送</button>
        </div>
      </div>
    </div>


</body>
<script>
  let lockReconnect = false // 是否允许重新连接
  let timeout = 1000 * 20 // 心跳间隔
  let reTimeout = 1000 * 10 // 重连间隔
  let timeoutTimer = null // 心跳定时器
  let reTimeoutTimer = null // 重连定时器
  let webSocket = null
  let uid = new Date().getTime()
  let chatBoxId = ''
  let getmessage = [] // 接受所有在线用户信息
  let websocketLine = 'ws://192.168.10.7:5001/'
  let localhostLine = 'http://192.168.10.7:5050/'

  // 创建并获取在线用户信息
  function createGetLine() {
    webSocket = new WebSocket(websocketLine + uid)
    webSocket.binaryType = 'arraybuffer';
  }
  // 获取消息接收时间
  function getDates() {
    let date = new Date()
    let hours = date.getHours()
    let minutes = date.getMinutes()
    return `${hours > 9 ? hours : '0' + hours}:${minutes > 9 ? minutes : '0' + minutes}`
  }

  // 点击返回用户列表
  function toback() {
    G('views').style.left = '0px';
    chatBoxId = '' // 退出聊天对象
  }
  // 发送聊天内容动作
  function tosend() {
    let msg = G('inputchat').value
    console.log('聊天记录chatBoxId:' + chatBoxId)
    console.log('聊天记录chat:' + msg)
    if (chatBoxId && msg !== '') {
      webSocket.send(JSON.stringify({
        receiver: chatBoxId,
        msg: msg
      }))
      G('inputchat').value = ''
    }
  }
  // 创建websocket连接
  function createWebSocket() {
    createGetLine()
    webSocket.onopen = function () {
      console.log('连接成功...')
      // 监听回车发送消息事件
      window.onkeypress = function (e) {
        if (e.keyCode === 13) {
          tosend();
        }
      }
    }
    webSocket.onerror = function () {
      console.log('连接失败...')
      reConnect()
    }
    // websocket收到信息的处理逻辑
    webSocket.onmessage = function (event) {
      if (event.data) {
        let res = JSON.parse(event.data)
        if (res && res.onlineUser) {
          getmessage = res.onlineUser
        }
        console.log('onmessage getmessage')
        console.log(res)
        switch (res.type) {
          // 当消息为用户列表
          case 'users':
            G('userList').innerHTML = res.onlineUser.map(val => {
              if (val == uid) {
                // return '<li class="userClass" id=' + val + '>me:' + val + '<span id=' + val + 'span></span></li>'
                G('headuser').innerHTML = val
              } else {
                return `<li class='userClass' id='${val}' onclick='userClick(${val})'>
                          <p>${val}</p>
                          <div>
                            <span id='${val}time'></span>
                            <span id='${val}span' class='redTip'></span>
                          </div>
                        </li>`
              }
            }).join('')
            break
          // 当消息为聊天信息
          case 'chat':
            console.log('chat chatBoxId:' + chatBoxId)
            if (chatBoxId) {
              // 当接受到的信息和当前聊天的对象不相同时
              if (res && res.from && chatBoxId !== res.from) {
                // 未同时建立通信的情况下接收到未知信息 做识别处理
                reloadChatInfo(getmessage)
              } else {
                reloadChatInfo()
              }
            } else {
              // 未同时建立通信的情况下接收到未知信息 做识别处理
              reloadChatInfo(getmessage)
            }
            break
          default:
        }
      }
    }
    webSocket.onclose = function (event) {
      console.log('连接已关闭...')
      console.log('websocket 断开: ' + event.code + ' ' + event.reason + ' ' + event.wasClean)
      reConnect()
    }
  }
  // 重连
  function reConnect() {
    if (lockReconnect) {
      return
    }
    lockReconnect = true
    // 没连接上会一直重连，设置延迟避免请求过多
    reTimeoutTimer && clearTimeout(reTimeoutTimer)
    reTimeoutTimer = setTimeout(function () {
      createWebSocket()
      lockReconnect = false
    }, reTimeout)
  }

  // 监听在线用户点击事件
  // G('userList').addEventListener('click', (e) => {
  //   console.log('e.target.id:' + e.target.id)
  //   chatBoxId = e.target.id.replace('time', '').replace('span', '')
  //   G(e.target.id + 'span').innerHTML = ''
  //   G(e.target.id + 'span').classList.remove('redTip')
  //   G('title').innerHTML = chatBoxId
  //   reloadChatInfo()
  // })
  function userClick(id) {
    chatBoxId = id + ''
    G(id + 'span').innerHTML = ''
    G(id + 'span').style.visibility = 'hidden';
    G('title').innerHTML = chatBoxId
    // 进入聊天内容位置
    G('views').style.left = '-100%';

    reloadChatInfo()
  }

  // 根据当前聊天用户重新获取聊天信息
  function reloadChatInfo(x) {
    console.log('x:' + x)
    console.log('chatBoxId:' + chatBoxId)
    if (!x) {
      console.log('!x')
      getDataMethod('get', localhostLine + 'getchat', {
        friendId: chatBoxId,
        id: uid,
      }).then(res => {
        let chatlist = JSON.parse(res)
        console.log('reloadChatInfo chatlist:' + JSON.stringify(chatlist))
        G('chatcontent').innerHTML = chatlist.map(val => {
          if (val.from == uid) {
            // return '<p class="me chatClass">' + val.msg + ':我</p>'
            return `<div class="me chatClass"><div class="mediv"><p class="username">${uid}</p><span class="message_content me_icon">${val.msg}</span></div><img class="meuserimg" src = "./favicon.ico"></div>`
          } else {
            // return `<p class="other chatClass">${val.from}:${val.msg}</p>`
            return `<div class="other chatClass"><img class="otheruserimg" src = "./favicon.ico"><div class="otherdiv"><p class="username">${val.from}</p><span class="message_content other_icon">${val.msg}</span></div></div>`
          }
        }).join('')
        console.log('clientHeight:' + G('chatcontent').clientHeight)
        console.log('scrollHeight:' + G('chatcontent').scrollHeight)
        // 聊天记录超高时，视窗跟随聊天位置滚动
        G('chatcontent').scrollTop = G('chatcontent').scrollHeight - G('chatcontent').clientHeight

      })
    } else {
      console.log('for x')
      for (let val of x) {
        if (val === uid) continue // 不需要循环自身的聊天记录
        console.log('for x:' + val)
        // if (uid !== val) {
        getDataMethod('get', localhostLine + 'getchat', {
          friendId: val,
          id: uid
        }).then(res => {
          let chatlist = JSON.parse(res)
          console.log('reloadChatInfo chatlist getmessage:' + JSON.stringify(chatlist))
          console.log('reloadChatInfo chatlist getmessage:' + chatlist.length)
          console.log('reloadChatInfo chatlist getmessage:' + val)
          let len = chatlist.filter((value, key, arr) => { // 过滤，获取已发送信息的用户数量，以此做未读信息提醒
            console.log('val filter' + value)
            return value.to == uid
          })
          let noshow = len.some((value, key, arr) => { // 与当前用户通讯时，不会出现未读信息提示
            return value.from == chatBoxId
          })
          console.log('len' + len + 'noshow:' + noshow)
          if (parseInt(len.length) > 0 && !noshow) {
            console.log(JSON.stringify(G(val + 'span')) + 'val:' + val + '_uid:' + uid)
            G(val + 'span').innerHTML = parseInt(len.length) <= 99 ? parseInt(len.length) : '99+'
            G(val + 'span').style.visibility = 'visible';

            G(val + 'time').innerHTML = getDates() // 更新信息发送过来的时间
          } else {
            // G(val + 'span').innerHTML = ''
            // G(val + 'span').style.visibility = 'hidden';
          }
        })
      }
    }
  }
  // 获取聊天信息的ajax请求
  function getDataMethod(method, url, params) {
    if (params) {
      let arr = []
      for (let key in params) {
        arr.push(key + '=' + params[key])
      }
      url = url + '?' + arr.join('&')
    }
    return new Promise((resolve, reject) => {
      let xml = new XMLHttpRequest()
      xml.open(method, url, false)
      xml.onreadystatechange = function () {
        if (xml.readyState == 4) {
          console.log(xml)
          if (xml.status == 200) {
            resolve(xml.response)
          } else {
            reject(new Error(xml.statusText))
          }

        }
      }
      xml.send()
    })
  }

  window.onload = function () {
    createWebSocket() // 创建websocket连接
  }

</script>

</html>